出处:掘金
原作者:ErpanOmer
跨源资源共享(CORS)的预检请求(Preflight Request)机制中,TTL 缓存机制的核心作用是减少不必要的 OPTIONS 请求,从而提高性能、降低服务器压力
在现代 Web 应用中,出于安全原因,浏览器会对跨源请求(Cross-Origin Requests) 实施限制。为解决合法跨域通信需求,W3C 提出了 CORS(Cross-Origin Resource Sharing)规范
某些跨源请求会被认为是“有风险的请求”,例如:
PUT , DELETE , PATCH 等非常规方法X-Custom-Header)Content-Type 不属于 application/x-www-form-urlencoded , multipart/form-data , text/plain 三种之一这类请求在正式发送之前,浏览器会自动先发送一个 OPTIONS 请求,即“预检请求(Preflight Request)”,以确认服务器是否允许这个跨域行为
虽然预检是为了安全,但频繁的预检请求带来了显著的性能问题:
举例说明:
OPTIONS /api/user(预检)Access-Control-Allow-* 头GET /api/user如果每次请求都进行一次预检,效率极低
为缓解上述问题,CORS 引入了 Access-Control-Max-Age 响应头,允许浏览器在一段时间内缓存预检请求的结果,从而跳过后续的 OPTIONS 请求
响应头定义:
Access-Control-Max-Age: 86400
表示该响应的预检结果可以被缓存 86400 秒(即 24 小时)
下面我们从浏览器实现角度,详细讲解 TTL 缓存如何影响预检逻辑
浏览器会以以下维度缓存预检请求的结果:
缓存记录会绑定在这些组合上。例如: 如果你请求 https://api.example.com/data,用 PUT 方法加了一个 X-Token 头,预检结果会单独为此组合缓存
缓存一般存储于浏览器的内存中,或者在某些情况下写入磁盘(开发者模式下通常禁用磁盘缓存)。当 TTL 时间到达后,缓存会被销毁
后端示例(Node.js + Express):
app.options('/api/data', (req, res) => {
res.set({
'Access-Control-Allow-Origin': 'https://example.com',
'Access-Control-Allow-Methods': 'GET, POST, PUT',
'Access-Control-Allow-Headers': 'Content-Type, X-Token',
'Access-Control-Max-Age': '86400' // 缓存一天
})
res.sendStatus(200)
})
后续 24 小时内,只要是相同条件的请求,不再触发 OPTIONS 请求
Access-Control-Max-Age 是服务器端的承诺,不是浏览器的强制缓存周期
部分浏览器会设定最大上限,即使你写了几天的时间,也可能被强制缩短。例如:
哪怕是 header 多了一个字段,method 变了,URL 路径变了,都会重新触发预检
Chrome 的“Disable cache”开启时,预检缓存会被禁用。很多人误以为设置失败,其实是开发者工具影响了行为
对高频接口(如每次点击都会调用的搜索接口),加上合理的 TTL,性能提升显著:
| 项目 | 建议 |
|---|---|
| 设置缓存 | 建议 Access-Control-Max-Age: 600 或更长 |
| 控制 header | 尽量精简自定义 header,减少触发预检概率 |
| method 选择 | 使用 GET 或 POST 且 Content-Type 为 application/x-www-form-urlencoded |
| 缓存命中优化 | 保持接口请求一致性(路径、方法、headers) |
| 环境检测 | 测试时注意关闭“Disable cache”选项 |
Access-Control-Max-Age 并不是什么神秘参数,而是浏览器为优化跨域请求性能所设计的 TTL 缓存机制。通过合理利用该机制,可以大幅减少不必要的 OPTIONS 请求,从而提升前端性能,优化用户体验